package com.wuyan.wurpc.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.wuyan.wurpc.RpcApplication;
import com.wuyan.wurpc.config.RpcConfig;
import com.wuyan.wurpc.constant.RpcConstant;
import com.wuyan.wurpc.fault.retry.RetryStrategy;
import com.wuyan.wurpc.fault.retry.RetryStrategyFactory;
import com.wuyan.wurpc.fault.tolerant.TolerantStrategy;
import com.wuyan.wurpc.fault.tolerant.TolerantStrategyFactory;
import com.wuyan.wurpc.loadbalancer.LoadBalancer;
import com.wuyan.wurpc.loadbalancer.LoadBalancerFactory;
import com.wuyan.wurpc.model.RpcRequest;
import com.wuyan.wurpc.model.RpcResponse;
import com.wuyan.wurpc.model.ServiceMetaInfo;
import com.wuyan.wurpc.protocol.*;
import com.wuyan.wurpc.registry.Registry;
import com.wuyan.wurpc.registry.RegistryFactory;
import com.wuyan.wurpc.serializer.JdkSerializer;
import com.wuyan.wurpc.serializer.Serializer;
import com.wuyan.wurpc.serializer.SerializerFactory;
import com.wuyan.wurpc.server.tcp.VertxTcpClient;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetSocket;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * 服务代理(JDK动态代理)
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("调用invoke()方法");
        // 指定序列化器
        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

        // 构造请求
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 序列化
            log.info("开始序列化rpcRequest: {}", rpcRequest);
            byte[] bodyBytes = serializer.serialize(rpcRequest);

            // 从注册中心获取服务提供者请求地址
            log.info("开始获取提供者请求地址");
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            log.info("serviceMetaInfoList ： {}", serviceMetaInfoList);
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                throw new RuntimeException("暂无服务地址");
            }

            //负载均衡
            log.info("负载均衡选择服务——————————————————————————————");
            LoadBalancer loadBalancer = LoadBalancerFactory.getLoadBalancer(rpcConfig.getLoadBalancer());
            //将调用方法名(请求路径)作为负载均衡参数
            Map<String, Object> requestParams = new HashMap<>();
            requestParams.put("methodName", rpcRequest.getMethodName());
            log.info("requestParams : {}", requestParams);
            ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList);
            log.info("获取成功，信息为：{}", selectedServiceMetaInfo);

            //发送TCP请求
            log.info("发送请求,TCP协议");
            //使用重试策略
            RpcResponse rpcResponse;
            try {
                log.info("重试策略选择——————————————————————————————————");
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                log.info("策略为：retryStrategy : {}", retryStrategy);
                rpcResponse = retryStrategy.doRetry(() ->
                        VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo)
                );
            }catch (Exception e){
                //容错机制
                log.info("开启容错机制————————————————————————————————————");
                TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                Map<String, Object> requestTolerantParamMap = new HashMap<>();
                requestTolerantParamMap.put("rpcRequest",rpcRequest);
                requestTolerantParamMap.put("selectedServiceMetaInfo",selectedServiceMetaInfo);
                requestTolerantParamMap.put("serviceMetaInfoList",serviceMetaInfoList);
                rpcResponse = tolerantStrategy.doTolerant(requestTolerantParamMap, e);
            }
//            try (HttpResponse httpResponse = HttpRequest.post(selectedServiceMetaInfo.getServiceAddress())
//                    .body(bodyBytes)
//                    .execute()) {
//                byte[] result = httpResponse.bodyBytes();
//                // 反序列化
//                RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
//                log.info("执行完成，得到结果rpcResponse: {}", rpcResponse);
//                return rpcResponse.getData();
//
            log.info("成功获取rpcResponse: {}", rpcResponse);
            return rpcResponse.getData();
        } catch (IOException e) {
            throw new RuntimeException("调用失败");
        }
    }
}